; This is the final revision, designed to work with a 64K system ROM
; that has the CP/M code in the upper 16KB page (C000-FFFF).
; The NMI BASIC formerly placed there is now loaded by loading the
; standard Spectrum BASIC in DRAM0 and then injecting in it the NMI routine
; which is stored separately in the free space at the end of the upper
; 16KB system ROM page, after the CP/M code. Then 3 bytes are also modified
; starting at 0066 in the BASIC code.

; Lines that must be modified for different video memory address
; are marked with --------@@@ at the end (23 lines in total)
; Video memory starts at C000 for CoBra startup config (both 64K and 80K configs)
; and at 4000 for standard Spectrum (emulator to test this assembled code)

; Memory locations used for temporary storage of variables:
; $8000 = Current hardware config ($64 if 64K, $80 if 80K)
; $8080-$808F = checksum results for SYSTEM ROM area $0000-$3FFF
; $8090-$809F = checksum results for SYSTEM ROM area $4000-$7FFF
; $80A0-$80AF = checksum results for SYSTEM ROM area $8000-$BFFF
; $80B0-$80BF = checksum results for SYSTEM ROM area $C000-$FFFF

CBASE	EQU	$0400	; character definitions table, base address
			; (the first 32 (20h) chars are non printable
			; so we don't use them, therefore the space
			; between 0400-04FF can be used for code)
	ORG	$0000
	DI
	LD	A,$80
	LD	R,A	; set bit 7 of R to 1 -> PO to 1
	LD	SP,$0000 ; set stack at memory top
	CALL	DELAY	; a delay to allow 8255 to come out of reset
	CALL	DELAY	; a delay to allow 8255 to come out of reset
	LD      A,$03	;
	OUT     ($E3),A	; Reset
	OUT     ($EB),A	; and disable
	OUT     ($F3),A	; Z80-CTC
	OUT     ($FB),A	; channels.
	LD	A,$92	; Control word for 8255: Mode Set flag=active,
			;	Group A: Select Mode 0,
			;		 Port A set to Input mode,
			;		 Port C (upper half) set to Output Mode,
			;	Group B: Select Mode 0,
			;		 Port B set to Input Mode,
			;		 Port C (lower half) set to Output Mode
	OUT     ($DF),A	; Send control word to 8255
	LD      A,$40
	OUT     ($FE),A	; write 40h to port C of 8255
			; (border black, "SO" to 0, "O5" to 0,
			;  "O6" to 1 to allow access to video memory
			;  in the 80K hw startup config)
	JR	MAIN
			;############# LOAD SYSTEM FROM ROM #######
			; input: A=00 -> bit 3 (TO=A15) -> OPUS+BASIC/DEVIL+NMI
			;	 A=08 -> bit 7 (SO=A14) -> OPUS/BASIC DEVIL/NMI
			; for OPUS:  LD A,$00: CALL RS: LD A,$08: JP RS
			; for BASIC: LD A,$00: CALL RS: LD A,$08: JP ST
			; for DEVIL: LD A,$00: CALL ST: LD A,$08: JP RS
			; for NMI:   LD A,$00: CALL ST: LD A,$08: JP ST
RS	ADD	A,$06	; reset bit 3 (TO=A15=0) or 7 (SO=A14=0)
	JR	SOTO
ST	ADD	A,$07	;   set bit 3 (TO=A15=1) or 7 (SO=A14=1)
SOTO	CP	$08
	OUT	($DF),A
	RET	C	; return if bit 3 processed
SYS	LD	HL,$6000
	LD	DE,$E000
	LD	BC,$2000
	LDIR		; copy system code upper half in upper half DRAM#0 if 64K RAM
			;############# 80K BASIC CONFIG ########
			; This routine needs to be present at this address ($0038)
			; in the BOOT ROM, for Spectrum programs to be loaded from disk
			; and run in CP/M 80K with the standard "CP/M game loader".
	ORG	$0038
	LD      HL,$4000	; 4000 is the start addr of BASIC EPROM in the startup hw config
	LD      DE,$8000	; 8000 is the start addr of DRAM#0 in the startup hw config
	LD      BC,$4000	; 4000 is the length of the SPECTRUM BASIC code (16 KB)
	LDIR		; copy system code into DRAM#0 for 80K RAM
NMISYS	EXX		; HL' contains the final jump address
	LD      A,$03	;
	OUT     ($E3),A	;
	OUT     ($EB),A	;
	OUT     ($F3),A	;
	OUT     ($FB),A	;
	XOR     A	;
	OUT     ($FD),A	;
	OUT     ($FE),A	; write 03 to port C of 8255
			; (set border to white, SO=0, O5=0, O6=0)
	LD      R,A	; set bit 7 of R to 0, prepare for BASIC hardware configuration
	JP      (HL)	; change hardware configuration with a jump to (HL)

			; ############ MAIN CODE  ############

			; ---------- DRAWING THE SCREEN ----------

MAIN	LD	HL,$8000 ; video mem is at C000 -------@@@
	LD	DE,$8001 ; video mem is at C000 -------@@@
	LD	BC,$7FFF
	LD	(HL),0
	LDIR
	LD	HL,$D800
	LD	DE,$D801
	LD	BC,$300
	LD	(HL),$06 ; INK=yellow, PAPER=black
	LDIR
	LD	HL,$DAE0 ; --- cfg info background ----@@@
	LD	DE,$DAE1 ;			-------@@@
	LD	BC,$19
	LD	(HL),$05 ; INK=yellow, PAPER=black
	LDIR
	LD	HL,$D842 ; --- title background -------@@@
	LD	DE,$D843 ;			-------@@@
	LD	BC,$1B
	LD	(HL),$16 ; INK=yellow, PAPER=red
	LDIR
	LD	HL,$D8A2 ; --- 64K section title bg ---@@@
	LD	DE,$D8A3 ;			-------@@@
	LD	BC,$0B
	LD	(HL),$1E ; INK=yellow, PAPER=magenta
	LDIR
	LD	HL,$D8B2 ; --- 80K section title bg ---@@@
	LD	DE,$D8B3 ;			-------@@@
	LD	BC,$0B
	LD	(HL),$0E ; INK=yellow, PAPER=blue
	LDIR
	LD	HL,$1700 ; --- config info text
	LD	DE,M_9
	CALL	MSG_PR
			 ; ---------- START CONFIG DETECTION ----------
	LD	HL,MEM_T
	LD	DE,$E000 ; if 80K hw config, this s/b in the VRAM (for "O6"=1)
	LD	BC,$20
	LDIR		; copy signature test string to $E000
	LD	A,$00
	OUT     ($FE),A	; write 00h to port C of 8255
			; (set "O6" to 0, if we are in 80K config then at
			; E000 there s/b now DRAM#1 instead of VRAM)
	LD	DE,MEM_T
	LD	HL,$E000
	LD	BC,$20
SGV	LD	A,(DE)
	CPI
	JP	PO,C64K	; config is 64K if entire signature string identical
	JR	NZ,C80K	; config is 80K if anything is different
	INC	DE
	JR	SGV
C64K	LD	HL,$171C ; --- configuration 64K
	LD	DE,M_10
	CALL	MSG_PR
	LD	HL,$D900 ; --- 64K section first item ---@@@
	LD	DE,$D901 ;			---------@@@
	LD	BC,$0E
	LD	(HL),$86 ; INK=yellow, PAPER=black, BLINK
	LDIR
	LD	HL,$DAFB ; --- config background --------@@@
	LD	DE,$DAFC ;			---------@@@
	LD	BC,$04
	LD	(HL),$1E ; INK=yellow, PAPER=magenta
	LDIR
	LD	A,$64
	LD	($8000),A ; (store computer config info at 8000)
	LD	IX,(T_LC) ; position of top item in left column (color attr)
	LD	IY,$01
	LD      A,$03	; border magenta, "SO" to 0, "O5" to 0, "O6" to 0
	OUT     ($FE),A	; ("O6" to 1 not req'd for access to video memory
			; in the 64K hw startup config)
	JR	NXT5
C80K	LD      A,$41	; border blue, "SO" to 0, "O5" to 0,
	OUT     ($FE),A	; "O6" to 1 to allow access to video memory
			; in the 80K hw startup config
	LD	HL,$171C ; --- configuration 80K
	LD	DE,M_11
	CALL	MSG_PR
	LD	HL,$D911 ; --- 80K section first item ---@@@
	LD	DE,$D912 ;			---------@@@
	LD	BC,$0E
	LD	(HL),$86 ; INK=yellow, PAPER=black, BLINK
	LDIR
	LD	HL,$DAFB ; --- config background --------@@@
	LD	DE,$DAFC ;			---------@@@
	LD	BC,$04
	LD	(HL),$0E ; INK=yellow, PAPER=blue
	LDIR
	LD	A,$80
	LD	($8000),A ; (store computer config info at 8000)
	LD	IX,(T_RC) ; position of top item in right column (color attr)
	LD	IY,$11
			; ---------- END CONFIG DETECTION ----------

NXT5	LD	HL,$0204 ; title
	LD	DE,M_1
	CALL	MSG_PR
	LD	HL,$0503 ; 64K section header
	LD	DE,M_2
	CALL	MSG_PR
	LD	HL,$0513 ; 80K section header
	LD	DE,M_3
	CALL	MSG_PR
	LD	HL,$0801 ; std boot (64k)
	LD	DE,M_4
	CALL	MSG_PR
	LD	HL,$0812 ; std boot (80k)
	LD	DE,M_4
	CALL	MSG_PR
	LD	HL,$0A01 ; std test (64k)
	LD	DE,M_6
	CALL	MSG_PR
	LD	HL,$0A12 ; std test (80k)
	LD	DE,M_6
	CALL	MSG_PR
	LD	HL,$0C01 ; homok test (64k)
	LD	DE,M_7
	CALL	MSG_PR
	LD	HL,$0C12 ; mastery boot (80k)
	LD	DE,M_5
	CALL	MSG_PR
			; ---------- KEYBOARD READING ----------
			;
KBD	LD	A,($8000)
	CP	$80
	JR	NZ,NOCPM
	LD	A,$FE	; test row KA8
	IN	A,($FE)
	RLA
	RLA
	RLA		; test bit K5 (CTRL key)
	JR	C,NOCPM
	LD	A,$EF	; test row KA12
	IN	A,($FE)
	RRA		; test bit K0 (0 key)
	JP	NC,CPM0	; CP/M with drive 0 as default if CTRL+0 pressed
	RRA		; test bit K1 (9 key)
	JP	NC,CPM1	; CP/M with drive 1 as default if CTRL+9 pressed
	RRA		; test bit K2 (8 key)
	JP	NC,CPM2	; CP/M with drive 2 as default if CTRL+8 pressed
	RRA		; test bit K3 (7 key)
	JP	NC,CPM3	; CP/M with drive 3 as default if CTRL+7 pressed
NOCPM	LD	A,$7F	; test row KA15
	IN	A,($FE)
	RLA
	RLA
	RLA		; test bit K5 (F4 key)
	CALL	NC,CRC	; do system ROM checksums
			; ********** ROM LOADING B.N.O.D ***********
	RLA		; test bit K4 (B key)
	JR	NC,BASIC ; load Spectrum Basic
	RLA		; test bit K3 (N)
	JR	NC,NMI	; load Spectrum Basic with NMI routine
	LD	A,$DF	; test row KA13
	IN	A,($FE)
	RRA
	RRA		; test bit K1 (O key)
	JR	NC,OPUS	; load OPUS
	LD	A,$FD	; test row KA9
	IN	A,($FE)
	RRA
	RRA
	RRA		; test bit K2 (D key)
	JR	NC,DEVIL ; load DEVIL
			; ********** ROM LOADING 1,2,3,4 ***********
	LD	A,$F7	; test row KA11
	IN	A,($FE)
	RRA		; test bit K0 (1 key)
	JR	NC,OPUS
	RRA		; test bit K1 (2 key)
	JR	NC,BASIC
	RRA		; test bit K2 (3 key)
	JR	NC,DEVIL
	RRA		; test bit K3 (4 key)
	JR	NC,NMI
			; ******************************************
	LD	A,$BF	; test row KA14
	IN	A,($FE)
	RRA		; check bit K0 (ENTER key)
	CALL	NC,SEL	; select and run boot code
	LD	A,$FE	; test row KA8
	IN	A,($FE)
	RRA		; check bit K0 (Caps Shift)
	JR	C,KBD	; if CapsShift not pressed restart loop
	LD	A,$EF	; test row KA12
	IN	A,($FE)
	RLA
	RLA
	RLA
	RLA		; test bit K4 (key 6, down arrow)
	PUSH	AF
	CALL	NC,DOWN
	POP	AF
	RLA		; test bit K3 (key 7, up arrow)
	CALL	NC,UP
	JR	KBD	; ############ END MAIN CODE ############

			; ########## ROM SYSTEM LOADING #########
			; --------- using port C parallel load
;OPUS	LD	A,$00	; SO=0, TO=0
;	JR	LOAD
;BASIC	LD	A,$80	; SO=1, TO=0
;	JR	LOAD
;DEVIL	LD	A,$08	; SO=0, TO=1
;	JR	LOAD
;NMI	LD	A,$88	; SO=1, TO=1	; no longer valid, now TO=0 to load regular BASIC in which NMI code will be injected after
;LOAD	OUT	($FE),A
;	LD	HL,$0000
;	EXX
;	JP	SYS
			; -------- using port C BSR Mode
OPUS	XOR	A
	CALL	RS
	JR	O_D
BASIC	XOR	A
	CALL	RS
	JR	B_N
DEVIL	XOR	A
	CALL	ST
O_D	LD	A,$08
	LD	HL,$0000
	EXX
	JP	RS
B_N	LD	A,$08
	LD	HL,$0000
	EXX
	JP	ST
		; Routine for NMI BASIC loading using a 64K SYSTEM ROM	
		; that has CP/M (12800 bytes) + NMI code in the last quarter
		; (C000-FFFF, where NMI BASIC used to be)
		; The NMI BASIC is now loaded by loading the regular BASIC
		; and then injecting the NMI code on top of it.
NMI	LD	A,$80
	OUT	($FE),A	; same as BASIC, set bit 7 (SO=A14=1) and reset bit 3 (TO=A15=0)
	LD	HL,$0000
	EXX
SYS1	LD	HL,$6000
	LD	DE,$E000
	LD	BC,$2000
	LDIR		; copy system code upper half in upper half DRAM#0 if 64K RAM
			;############# 80K BASIC CONFIG #############
	LD      HL,$4000	; 4000 is the start addr of BASIC EPROM in the startup hw config
	LD      DE,$8000	; 8000 is the start addr of DRAM#0 in the startup hw config
	LD      BC,$4000	; 4000 is the length of the SPECTRUM BASIC code (16 KB)
	LDIR		; copy system code into DRAM#0 for 80K RAM
			; ############ NMI CODE INJECTION ###########
	LD	A,$88	; SO=1, TO=1, access the CP/M ROM page
	OUT	($FE),A
	LD	HL,$720F	; NMI code address in the CP/M ROM page
	LD	DE,$F86F	; NMI code injection start address in 64K config
	LD	BC,$013C	; NMI code length
	LDIR
	LD	HL,$720F	; NMI code address in the CP/M ROM page
	LD	DE,$B86F	; NMI code injection start address in 80K config
	LD	BC,$013C	; NMI code length
	LDIR
	LD	HL,$8066	; \
	LD	(HL),$C3	;  \ modify 3 bytes at $0066 in BASIC
	LD	HL,$3880	;  / to call the NMI code injected
	LD	($8067),HL	; /
	JP	NMISYS		; jump to already existing copy of code below (to save space)
;	EXX		; HL' contains the final jump address
;	LD      A,$03	;
;	OUT     ($E3),A	;
;	OUT     ($EB),A	;
;	OUT     ($F3),A	;
;	OUT     ($FB),A	;
;	XOR     A	;
;	OUT     ($FD),A	;
;	OUT     ($FE),A	; write 03 to port C of 8255
;			; (set border to white, SO=0, O5=0, O6=0)
;	LD      R,A	; set bit 7 of R to 0, prepare for BASIC hardware configuration
;	JP      (HL)	; change hardware configuration with a jump to (HL)
			;
			; ######### CP/M ROM LOADING #########
CPM0	LD	A,$00	; default drive 0
	JR	CPM
CPM1	LD	A,$01	; default drive 1
	JR	CPM
CPM2	LD	A,$02	; default drive 2
	JR	CPM
CPM3	LD	A,$03	; default drive 3
CPM	LD	B,$8A	; bit2=1 (BORDER=RED), bit3=TO=A15=1 and bit7=SO=A14=1 to select 16K SYSTEM ROM with CPM
	LD	C,$FE	; port C address
	OUT	(C),B	; set
	LD	HL,$4000
	LD	DE,$8000
	LD	BC,$3200
	LDIR
	LD	HL,$8004 ; DSK Byte, location used as mailbox from BOOT to CP/M for user choice of default drive
	LD	(HL),A	; set default CP/M logical drive
	LD	HL,$0000 ; jump address in upcoming CP/M config
	LD	A,$40
	OUT	($FE),A	; set O6=1 for CP/M config
	XOR	A
	LD	R,A
	JP	(HL)
			; ############ CHAR_PRINT ############
			; INPUTS:
			; H = row to print character (0-23)
			; L = column to print character (0-31)
			; C = character code to print
CHR_PR	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF
	LD	DE,0	; DE = final screen mem addr
	LD	A,H
	LD	B,3
L1	SRL	A	; 0 -> A ->CY
	RR	E	; CY -> E -> CY
	DJNZ	L1	; row nr in bits 7,6,5 of E
	LD	A,E
	ADD	A,L	; add E to L
	LD	E,A	; store result in E  ##### lower final screen addr
	LD	A,H
	OR	$C0	; video mem is at C000 -------@@@
	AND	$F8	; reset lower 3 bits
	LD	D,A	; store result in D  ##### upper final screen addr
	LD	HL,CBASE ; character definitions base addr
	LD	B,0	; BC = character to print on 16 bit
	RL	B	; reset CY
	RL	C	; ] multiply
	RL	B	; ] contents of BC
	RL	C	;
	RL	B	;
	RL	C	;
	RL	B	; ] by 8
	ADD	HL,BC	; HL = char addr in char table
	LD	B,8	; counter for 8 lines TV of a char
L2	LD	A,(HL)	;\
	LD	(DE),A	; | print
	INC	HL	; | the
	INC	D	; | character
	DJNZ	L2	;/
	POP	AF
	POP	BC
	POP	DE
	POP	HL
	RET
			; ############ MSG PRINT ############
			; INPUTS:
			; H = start row to print message (0-23)
			; L = start column to print message (0-31)
			; DE = message start address (ends with $00)
MSG_PR	LD	A,(DE)
	LD	C,A
	LD	A,0
	CP	C
	RET	Z	; return if char code is 0
	LD	A,$1F
	CP	L
	RET	M	; return if out of screen
	CALL	CHR_PR
	INC	L
	INC	DE
	JR	MSG_PR
			; ######## KEYBOARD NON-PRESSED TEST ########
KEY_NP	LD	A,$FE	; test row KA8
	IN	A,($FE)
	AND	$3F
	CP	$3F	; check if any key pressed
	JR	NZ,KEY_NP	; if any key  still pressed read kbd again
	LD	A,$EF	; test row KA12
	IN	A,($FE)
	AND	$3F
	CP	$3F	; check if any key pressed
	JR	NZ,KEY_NP	; if any key still pressed, read kbd again
	LD	B,$03
DL1	CALL	DELAY
	DJNZ	DL1
	RET

			; ########## HEX2ASCII #########
			; INPUTS:
			; A = byte to print as ASCII hex digits
			; OUTPUTS:
			; B = upper digit ascii
			; C = lower digit ascii
H2A	LD	B,A
	SRL	B
	SRL	B
	SRL	B
	SRL	B	; B=upper digit
	AND	$0F
	LD	C,A	; C=lower digit

	LD	A,B
	CALL	TRAN
	LD	B,A	; B=upper hex digit as ascii
	LD	A,C
	CALL	TRAN
	LD	C,A	; B=lower hex digit as ascii

	RET
			; ###### translate 4-bit hex digit in A into ASCII value
TRAN	CP	$0A	; if A=00...09
	JR	C,NXT3	; skip next line
	ADD	A,$07
NXT3	ADD	A,$30	; A=ascii index
	RET

			; ########## HEXPRINT ##########
			; INPUTS:
			; H = row to print character (0-23)
			; L = column to print character (0-31)
			; B = upper digit ascii
			; C = lower digit ascii
HEX_PR	LD	A,C
	LD	C,B
	CALL	CHR_PR
	INC	L
	LD	C,A
	CALL	CHR_PR
	INC	L
	RET

			; ######### CHECKSUM PROCEDURE #########
			; INPUTS:
			; IX = storage start addr for checksums (2 bytes per 2KB checked)
CHS	PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
	EXX
	PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
	EXX

;	LD      IX,$8080 ; storage addr for checksums
	LD      B,$08	; loop counter
	LD      HL,$4000 ; BASIC ROM start addr (HL=mem pointer)
L2K	PUSH    BC	; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <---|
	LD      DE,$0800 ;							|
	EXX		; *****							|
	LD      HL,$0000 ; HL'=0000 to start, HL' stores CRC value		|
	EXX		; *****							|
LCH1	LD      A,(HL)	; <--- <--- <--- <--- <--- <----| read from (4000)	|
	EXX		; *****				|			|
	LD      B,$08	; loop counter			|			|
	LD      C,A	; C=data byte			|			|
LCH2	RLC     C	; CY = bit 7<--- <--- <---|	|			|
	SBC     A,A	; A=00-CY		  |	|			|
	LD      E,A	; E=00(bit7=0) or FF	  |	|			|
	LD      A,L	; (loop starts w L'=00)   |	|			|
	AND     $40	;			  |	|			|
	SUB     $01	;			  |	|			|
	SBC     A,A	; A=00(Lbit6=1) or FF	  |	|			|
	XOR     E	;			  |	|			|
	LD      E,A	; E=00 if bit7!=Lbit6	  |	|			|
	LD      A,H	; (loop starts w H'=00)   |	|			|
	AND     $01	;			  |	|			|
	SUB     $01	;			  |	|			|
	SBC     A,A	; A=00(Hbit1=1) or FF	  |	|			|
	XOR     E	;			  |	|			|
	LD      E,A	; E=00 if [(Hbit1=1)&(bit7!=Lbit6)]||[(Hbit1=0)&(bit7=Lbit6)]
	LD      A,H	;			  |	|			|
	AND     $08	;			  |	|			|
	SUB     $01	;			  |	|			|
	SBC     A,A	; A=00(Hbit3=1) or FF	  |	|			|
	XOR     E	;			  |	|			|
	LD      E,A	;			  |	|			|
	LD      A,H	;			  |	|			|
	AND     $80	;			  |	|			|
	SUB     $01	;			  |	|			|
	SBC     A,A	; A=00(Hbit7=1) or FF	  |	|			|
	XOR     E	;			  |	|			|
	RLCA		;			  |	|			|
	RL      L	;			  |	|			|
	RL      H	;			  |	|			|
	DJNZ    LCH2	; ---> 8 loops -----> --->|	|			|
	EXX		; *****				|			|
	INC     HL	; increment mem pointer		|			|
	DEC     DE	; decrement loop counter	|			|
	LD      A,D	;\ test if			|			|
	OR      E	;/ DE=0000			|			|
	JR      NZ,LCH1	; ---> ---> 800h loops ---> --->|			|
	EXX		; *****							|
	LD	(IX+0),H ; store high byte of checksum				|
	LD	(IX+1),L ; store low byte of checksum				|

	PUSH	AF
	PUSH	BC
	LD	HL,($8002)
	LD	A,(IX+0)
	CALL	H2A
	CALL	HEX_PR
	LD	A,(IX+1)
	CALL	H2A
	CALL	HEX_PR
	LD	($8002),HL
	POP	BC
	POP	AF

	INC	IX	;							|
	INC	IX	;							|
	EXX		; *****							|
	POP     BC	; B = loop counter					|
	DJNZ    L2K	; ---> 8 loops -----> ---> ---> ---> ---> ---> ---> --->|

	EXX
	POP     HL
	POP	DE
	POP	BC
	POP	AF
	EXX
	POP     HL
	POP	DE
	POP	BC
	POP	AF

	RET
			; ########### CHECKSUMS ##########
CRC	PUSH	AF
	PUSH	BC
	PUSH	IX

	LD	HL,$1000 ; ROM checksum message
	LD	DE,M_12
	CALL	MSG_PR

	LD	IX,$8080
	LD	A,($8000)
	CP	$64
	JR	Z,CKS1
	LD	B,$41	; TO=0,SO=0 select ROM 0000-3FFF (80K config)
	JR	CKS2
CKS1	LD	B,$43	; TO=0,SO=0 select ROM 0000-3FFF (64K config)

CKS2	LD	HL,$1017 ; address range being checksumed, 0000-3FFF
	LD	DE,M_13
	CALL	MSG_PR
	LD	HL,$1200
	LD	($8002),HL
	CALL	DO1	; do checksums for ROM 0000-3FFF, store at 8080-808F
	SET	7,B	; TO=0,SO=1 select ROM 4000-7FFF
	LD	HL,$1017 ; address range being checksumed, 4000-7FFF
	LD	DE,M_14
	CALL	MSG_PR
	LD	HL,$1300
	LD	($8002),HL
	CALL	DO1	; do checksums for ROM 4000-7FFF, store at 8090-809F
	RES	7,B
	SET	3,B	; TO=1,SO=0 select ROM 8000-BFFF
	LD	HL,$1017 ; address range being checksumed, 8000-BFFF
	LD	DE,M_15
	CALL	MSG_PR
	LD	HL,$1400
	LD	($8002),HL
	CALL	DO1	; do checksums for ROM 8000-BFFF, store at 80A0-80AF
	SET	7,B	; TO=1,SO=1 select ROM C000-FFFF
	LD	HL,$1017 ; address range being checksumed, C000-FFFF
	LD	DE,M_16
	CALL	MSG_PR
	LD	HL,$1500
	LD	($8002),HL
	CALL	DO1	; do checksums for ROM C000-FFFF, store at 80B0-80BF
			; ---------- erase current range -----------
	LD	HL,$1017 ; address range being checksumed, C000-FFFF
	LD	DE,M_18
	CALL	MSG_PR
			; ---------- highlight groups of 4 digits ----------
	LD	HL,M_13	; smart reuse of already available data
	LD	BC,$04
	LD	DE,$DA44 ;							---------@@@
	EXX
	LD	B,$10
REP1	EXX
	PUSH	HL
	PUSH	BC
	LDIR
	POP	BC
	POP	HL
	INC	DE
	INC	DE
	INC	DE
	INC	DE
	EXX
	DJNZ	REP1
	EXX	
			; --------------------------------------------------
	POP	IX
	POP	BC
	POP	AF
	RET

DO1	PUSH	BC	; backup current ROM zone selection
	LD	A,B
	OUT	($FE),A
	CALL	CHS	; checksums for ROM 0000-4000 stored at 8080-808F
	POP	BC	; restore current ROM zone selection
	RET

			; ####### MOVE SELECTION UP ######
UP	CALL	KEY_NP
	PUSH	IX
	LD	A,($8000)
	CP	$64
	LD	HL,T_LC
	JR	Z,NXT6
	LD	HL,T_RC
NXT6	POP	BC
	LD	A,C
	CP	(HL)	; compare low byte of T_LC
	JR	NZ,NXT1 ; skip fwd if diff
	INC	HL
	LD	A,B
	CP	(HL)	; compare high byte of T_LC
	RET	Z	; if already on top item, exit
NXT1	PUSH	IX
	POP	HL
	LD	D,H
	LD	E,L
	INC	DE
	LD	BC,$0E
	LD	(HL),$06 ; INK=yellow, PAPER=black, BLINK=OFF
	LDIR		; turn BLINK OFF
	PUSH	IX
	POP	HL
	LD	A,(R_I)
	LD	DE,0
	LD	E,A
	RLC	D	; reset CY
	SBC	HL,DE
	PUSH	HL
	POP	IX	; update IX with current pos of selected item
	DEC	IY
	LD	D,H
	LD	E,L
	INC	DE
	LD	BC,$0E
	LD	(HL),$86 ; INK=yellow, PAPER=black, BLINK
	LDIR		; turn BLINK ON
	RET
	
			; ####### MOVE SELECTION DOWN ######
DOWN	CALL	KEY_NP
	PUSH	IX
	LD	A,($8000)
	CP	$64
	LD	HL,B_LC
	JR	Z,NXT7
	LD	HL,B_RC
NXT7	POP	BC
	LD	A,C
	CP	(HL)	; compare low byte of B_LC
	JR	NZ,NXT2 ; skip fwd if diff
	INC	HL
	LD	A,B
	CP	(HL)	; compare high byte of B_LC
	RET	Z	; if already on bottom item, exit
NXT2	PUSH	IX
	POP	HL
	LD	D,H
	LD	E,L
	INC	DE
	LD	BC,$0E
	LD	(HL),$06 ; INK=yellow, PAPER=black, BLINK=OFF
	LDIR		; turn BLINK OFF
	PUSH	IX
	POP	HL
	LD	A,(R_I)
	LD	DE,0
	LD	E,A
	ADD	HL,DE
	PUSH	HL
	POP	IX	; update IX with current pos of selected item
	INC	IY
	LD	D,H
	LD	E,L
	INC	DE
	LD	BC,$0E
	LD	(HL),$86 ; INK=yellow, PAPER=black, BLINK
	LDIR		; turn BLINK ON
	RET
			; ########### BOOT SELECT ########
SEL	PUSH	BC	; backup BC
	PUSH	IY
	POP	BC
	LD	A,C
	CP	$01
	JP	Z,BOOT64
	CP	$02
	JP	Z,TEST64
	CP	$03
	JP	Z,HOMOK
	CP	$11
	JP	Z,BOOT80
	CP	$12
	JP	Z,TEST64
	CP	$13
	JP	Z,MASTY
	POP	BC	; restore BC
	RET
BOOT64	EQU	$0800	; address of standard 64K boot
BOOT80	EQU	$1000	; address of standard 80K boot
HOMOK	EQU	$1800	; address of HOMOK test (64K)
TEST64	EQU	$2000	; address of STD test (64K)
MASTY	EQU	$2800	; address of MASTERY boot (80K)

			; ########### MESSAGES ###########
	ORG	$6E0
M_1	DEFM	"COBRA BOOT CODE SELECTOR"
	DEFB	0
M_2	DEFM	"64K CONFIG"
	DEFB	0
M_3	DEFM	"80K CONFIG"
	DEFB	0
M_4	DEFM	"STANDARD BOOT"
	DEFB	0
M_5	DEFM	"MASTERY BOOT"
	DEFB	0
M_6	DEFM	"STANDARD TEST"
	DEFB	0
M_7	DEFM	"HOMOK TEST"
	DEFB	0
M_8	DEFM	"MODIFIED BOOT"
	DEFB	0
M_9	DEFM	"THIS COMPUTER'S CONFIG IS:"
	DEFB	0
M_10	DEFM	"64K"
	DEFB	0
M_11	DEFM	"80K"
	DEFB	0
M_12	DEFM	"SYSTEM ROM CHECKSUM:"
	DEFB	0
M_13	DEFM	"0000-3FFF"
	DEFB	0
M_14	DEFM	"4000-7FFF"
	DEFB	0
M_15	DEFM	"8000-BFFF"
	DEFB	0
M_16	DEFM	"C000-FFFF"
	DEFB	0
M_17	DEFM	"THIS BOOT ROM CHECKSUM:"
	DEFB	0
M_18	DEFM	"         "
	DEFB	0
			; ########### MEMORY TEST SIGNATURE ###########
MEM_T	DEFM	"*COBRA*HARDWARE*CONFIG*DETECTOR*"
			; ########### MENU SCREEN POSITIONS ###########
T_LC	DEFW	$D900	; top item left column (color attr start) -------@@@
T_RC	DEFW	$D911	; top item right column (color attr start) ------@@@
			; ***** these are values for 4 items per menu *****
;B_LC	DEFW	$D9C0	; bottom item left column (color attr start) ----@@@
;B_RC	DEFW	$D9D1	; bottom item right column (color attr start) ---@@@
			; ***** these are values for 3 items per menu *****
B_LC	DEFW	$D980	; bottom item left column (color attr start) ----@@@
B_RC	DEFW	$D991	; bottom item right column (color attr start) ---@@@

R_I	DEFB	$40	; row interval (color attr addr diff)

			; ########### CHARACTER DEFINITIONS ###########
	ORG	$500
C_SP	DEFB	0,0,0,0,0,0,0,0

	ORG	$508	; to save space, this little routine is placed between the SPACE and APOSTROPHE char definitions
DELAY	EXX
	LD	BC,$1FFF
	LD	DE,$9001
	LD	HL,$9000
	LD	(HL),0
	LDIR
	EXX
	RET

	ORG	$538
C_APST	DEFB	$18,$18,$10,$00,$00,$00,$00
	DEFB	0

	ORG	$568
C_MINUS	DEFB	$00,$00,$00,$7E,$7E,$00,$00
	DEFB	0

	ORG	$580
C_0	DEFB	$3C,$42,$46,$6A,$72,$62,$3C
	DEFB	0
C_1	DEFB	$18,$30,$50,$18,$18,$18,$18
	DEFB	0
C_2	DEFB	$3C,$42,$02,$1C,$70,$60,$7E
	DEFB	0
C_3	DEFB	$3C,$42,$02,$1C,$06,$46,$3C
	DEFB	0
C_4	DEFB	$40,$40,$48,$7E,$0C,$0C,$0C
	DEFB	0
C_5	DEFB	$7C,$60,$60,$7C,$02,$02,$7C
	DEFB	0
C_6	DEFB	$3C,$42,$40,$7C,$62,$62,$3C
	DEFB	0
C_7	DEFB	$7E,$02,$02,$0E,$1C,$38,$70
	DEFB	0
C_8	DEFB	$3C,$62,$62,$3C,$62,$62,$3C
	DEFB	0
C_9	DEFB	$3C,$62,$62,$3E,$02,$62,$3C
	DEFB	0

	ORG	$5D0
C_COLN	DEFB	$00,$18,$18,$00,$18,$18,$00
	DEFB	0

	ORG	$608
C_A	DEFB	$3C,$42,$42,$62,$7E,$62,$62
	DEFB	0
C_B	DEFB	$7C,$42,$42,$7C,$62,$62,$7C
	DEFB	0
C_C	DEFB	$3C,$42,$40,$60,$60,$62,$3C
	DEFB	0
C_D	DEFB	$7C,$42,$42,$62,$62,$62,$7C
	DEFB	0
C_E	DEFB	$7E,$40,$40,$78,$60,$60,$7E
	DEFB	0
C_F	DEFB	$7E,$40,$40,$7C,$60,$60,$60
	DEFB	0
C_G	DEFB	$3C,$42,$40,$60,$6E,$66,$3C
	DEFB	0
C_H	DEFB	$42,$42,$42,$7E,$62,$62,$62
	DEFB	0
C_I	DEFB	$3C,$10,$10,$18,$18,$18,$3C
	DEFB	0
C_J	DEFB	$1E,$04,$04,$0C,$6C,$6C,$38
	DEFB	0
C_K	DEFB	$46,$44,$48,$70,$68,$64,$66
	DEFB	0
C_L	DEFB	$40,$40,$40,$60,$60,$60,$7E
	DEFB	0
C_M	DEFB	$42,$66,$5E,$6A,$62,$62,$62
	DEFB	0
C_N	DEFB	$42,$62,$52,$6A,$66,$62,$62
	DEFB	0
C_O	DEFB	$3C,$42,$42,$62,$62,$62,$3C
	DEFB	0
C_P	DEFB	$7C,$42,$42,$62,$7C,$60,$60
	DEFB	0
C_Q	DEFB	$3C,$42,$42,$62,$6A,$64,$3A
	DEFB	0
C_R	DEFB	$7C,$42,$42,$62,$7C,$6C,$66
	DEFB	0
C_S	DEFB	$3C,$62,$60,$3C,$02,$62,$3C
	DEFB	0
C_T	DEFB	$7E,$10,$10,$18,$18,$18,$18
	DEFB	0
C_U	DEFB	$42,$42,$42,$62,$62,$62,$3C
	DEFB	0
C_V	DEFB	$42,$42,$42,$62,$62,$34,$18
	DEFB	0
C_W	DEFB	$42,$42,$4A,$6A,$6A,$76,$24
	DEFB	0
C_X	DEFB	$62,$62,$34,$08,$34,$62,$62
	DEFB	0
C_Y	DEFB	$42,$42,$42,$24,$18,$18,$18
	DEFB	0
C_Z	DEFB	$7E,$04,$08,$10,$20,$60,$7E
	DEFB	0